Skip to content

feat: add @constructive-io/fetch package#81

Merged
pyramation merged 1 commit intomainfrom
feat/fetch-package
Apr 28, 2026
Merged

feat: add @constructive-io/fetch package#81
pyramation merged 1 commit intomainfrom
feat/fetch-package

Conversation

@pyramation
Copy link
Copy Markdown
Contributor

Summary

New @constructive-io/fetch package — a lean isomorphic fetch wrapper that fixes *.localhost subdomain handling in Node.js.

Problem: Node.js has two issues with *.localhost subdomains that break local development with the Constructive GraphQL server's subdomain routing:

  1. DNS — fetch('http://auth.localhost:3000/') throws ENOTFOUND (nodejs/node#50871)
  2. Host header — Node's fetch (undici) treats Host as forbidden and silently drops it

Solution: createFetch() returns a fetch function that:

  • In Node.js: uses node:http/node:https for *.localhost URLs (fixing DNS + Host header), delegates everything else to globalThis.fetch
  • In browsers: returns globalThis.fetch as-is (browsers handle *.localhost natively)

Zero dependencies. ~100 lines of source code. Designed to replace @constructive-io/node as the default fetch for generated SDK clients (see constructive#1033).

API

import { createFetch } from '@constructive-io/fetch';

const fetch = createFetch();
const res = await fetch('http://auth.localhost:3000/graphql', { ... });

Review & Testing Checklist for Human

  • Verify the *.localhostlocalhost rewrite + Host header injection works against a real PostGraphile server with subdomain routing
  • Confirm non-localhost URLs pass through to globalThis.fetch without modification
  • Review node:http request handling covers all needed fetch options (method, headers, body, signal)

Notes

  • Uses synchronous require('node:http') rather than async import() — works in CJS (the monorepo target) and avoids bundler analysis issues
  • The result of createFetch() is cached (singleton) — safe to call repeatedly
  • 8 tests passing covering: subdomain detection, Host header preservation, plain localhost passthrough, content-type forwarding

Link to Devin session: https://app.devin.ai/sessions/0e3a5bf6765a4c979b64ef786a813225
Requested by: @pyramation

Isomorphic fetch wrapper that resolves *.localhost subdomains and
preserves Host headers across Node.js and browsers.

In Node.js, *.localhost URLs are handled via node:http/node:https
(fixing DNS resolution + Host header). Everything else delegates
to globalThis.fetch. In browsers, it's a passthrough.

Zero dependencies. Designed to replace @constructive-io/node as the
default fetch for generated SDK clients.
@devin-ai-integration
Copy link
Copy Markdown

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@pyramation pyramation merged commit ec3746d into main Apr 28, 2026
44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant